package com.second.client.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.second.client.config.RsaKeyProperties;
import com.second.client.dao.ClientUserDao;
import com.second.client.pojo.dto.ClientDto;
import com.second.client.pojo.entity.ClientIntegration;
import com.second.client.pojo.entity.ClientLevel;
import com.second.client.pojo.entity.ClientUser;
import com.second.client.pojo.vo.*;
import com.second.client.service.ClientIntegrationService;
import com.second.client.service.ClientLevelService;
import com.second.client.service.ClientUserService;
import com.second.client.util.NativePlace;
import com.second.client.util.PageData;
import com.second.client.util.SendSmsUtils;
import com.second.client.util.WebUtil;
import com.second.common.pojo.R;
import com.second.common.util.JwtUtil;
import com.second.common.util.Payload;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.yaml.snakeyaml.events.Event;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * (ClientUser)表服务实现类
 *
 * @author LvYongQi
 * @since 2021-07-23 09:53:39
 */
@Service("clientUserService")
public class ClientUserServiceImpl extends ServiceImpl<ClientUserDao, ClientUser> implements ClientUserService {

    @Resource
    private ClientUserDao clientUserDao;
    @Resource
    private ClientLevelService clientLevelService;
    @Resource
    private ClientIntegrationService clientIntegrationService;
    /**
     * Redis服务对象
     */
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RsaKeyProperties rsaKeyProperties;

    /**
     * 修改客户积分，返回结果
     */
    private boolean update ;

    /**
     * 查询所有的客户信息
     * @param pageVo 分页工具类
     * @param fuzzyQueryVo 模糊查询工具类
     * @return 所有数据
     */
    @Override
    public R getAllClient(PageVo pageVo, FuzzyQueryVo fuzzyQueryVo) {

        // 获取mybatis-plus封装的分页工具类
        Page<ClientUser> page = new Page<>(pageVo.getCurrentPage(),pageVo.getPageSize());
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();

        // 判断前端是否传递了模糊查询的规则
        if (fuzzyQueryVo.getLevel() != null) {
            queryWrapper.eq("level",fuzzyQueryVo.getLevel());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getUsername())) {
            queryWrapper.like("username",fuzzyQueryVo.getUsername());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getCardNumber())) {
            queryWrapper.eq("card_number",fuzzyQueryVo.getCardNumber());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getClientName())) {
            queryWrapper.like("client_name",fuzzyQueryVo.getClientName());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getPhone())) {
            queryWrapper.eq("phone",fuzzyQueryVo.getPhone());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getAddress())) {
            queryWrapper.eq("address",fuzzyQueryVo.getAddress());
        }
        if (StringUtils.isNotEmpty(fuzzyQueryVo.getSex())) {
            queryWrapper.eq("sex",fuzzyQueryVo.getSex());
        }

        return PageData.conditionalPage(clientUserDao,page,queryWrapper);

    }

    /**
     * 根据客户Id，改变客户状态
     * @param statusVo 工具类，客户id、状态id
     * @return 修改成功或失败
     */
    @Override
    public R changeClientStaus(ChangeStatusVo statusVo) {
        boolean update = clientUserDao.changeClientStaus(statusVo);
        if (update == true) {
            return R.success();
        }
        return R.error();
    }

    /**
     * 根据客户Id修改客户成长值
     *@param statusVo 工具类，客户id、成长值
     *@return 修改成功或失败
     */
    @Override
    @Transactional
    public R changeGrowthById(ChangeStatusVo statusVo) {
        // 根据客户的id，增加成长值
        boolean update = clientUserDao.changeGrowthById(statusVo);
        if (update == true) {
            // 根据id查询出客户的成长值
            double growth = this.getById(statusVo.getId()).getGrowth();
            // 取出所有的等级信息
            List<ClientLevel> levelList = clientLevelService.getAll().getListData();
            for (ClientLevel clientLevel : levelList) {
                // 判断该成长值对应的等级
                if (growth <= clientLevel.getGrowthPoint()) {
                    // 根据客户的成长值修改对应的等级
                    clientUserDao.updateLevelByGrowth(statusVo.getId(), clientLevel.getLevelId());
                    return R.success();
                }
            }

        }
        return R.error();
    }

    /**
     * 客户注册
     * @param registerClientVo 注册工具类
     * @return 注册结果
     */
    @Override
    public R registerClient(RegisterClientVo registerClientVo) {
        // 判断手机号或者账号是否重复
        int one = clientUserDao.getOneByUsernameOrPhone(registerClientVo.getUsername(),
                registerClientVo.getPhone(),"#");
        if (one > 0) {
            return R.error().putObject("当前账号已注册");
        }else {
            // 判断验证码是否正确
            if (!StringUtils.equals(registerClientVo.getCode(),
                    stringRedisTemplate.opsForValue().get(
                    registerClientVo.getPhone()))) {
                return R.error().putObject("验证码错误");
            } else {
                // 创建一个用户对象
                ClientUser clientUser = new ClientUser();
                clientUser.setUsername(registerClientVo.getUsername());
                clientUser.setPhone(registerClientVo.getPhone());

                // 使用MD5对密码进行加密
                Md5Hash md5Hash = new Md5Hash(registerClientVo.getPassword(),
                        registerClientVo.getUsername(),2);
                clientUser.setPassword(md5Hash.toString());

                // 将用户信息，保存到数据库中
                boolean save = this.save(clientUser);
                if (save == true) {
                    return R.success().putObject("注册成功");
                }
            }
        }
        return R.error().putObject("注册失败");
    }

    /**
     * 增加客户积分
     * @param updateIntegrationVo 修改客户积分工具类
     * @return 增加结果
     */
    @Override
    @Transactional
    public R addIntegration(UpdateIntegrationVo updateIntegrationVo) {
        // 获取客户历史积分
        Integer history = this.getIntegration(updateIntegrationVo);
        // 增加客户积分
        update = clientUserDao.addIntegration(updateIntegrationVo.getId(),
                updateIntegrationVo.getIntegration());
        // 获取客户增加后的积分
        Integer existing = this.getIntegration(updateIntegrationVo);

        // 获取积分记录实例化对象
        ClientIntegration clientIntegration = this.getClientIntegration(updateIntegrationVo,1,
                history,existing);

        // 增加一条积分记录，返回增加结果
        return this.insertClientIntegration(clientIntegration);

    }

    /**
     *  减少客户积分
     * @param updateIntegrationVo 修改客户积分工具类
     * @return 减少结果
     */
    @Override
    @Transactional
    public R reduceIntegration(UpdateIntegrationVo updateIntegrationVo) {

        // 获取客户历史积分
        Integer history = this.getIntegration(updateIntegrationVo);
        // 减少客户积分
        update = clientUserDao.reduceIntegration(updateIntegrationVo.getId(),
                updateIntegrationVo.getIntegration());
        // 获取客户增加后的积分
        Integer existing = this.getIntegration(updateIntegrationVo);

        // 获取积分记录实例化对象
        ClientIntegration clientIntegration = this.getClientIntegration(updateIntegrationVo,0,
                history,existing);

        // 增加一条积分记录，返回增加结果
        return this.insertClientIntegration(clientIntegration);

    }

    /**
     * 发送验证码
     * @param phone 客户手机号
     * @return 生成结果
     */
    @Override
    public R sendSms(String phone) {
        // 使用工具类获取生成的验证码
        String code = SendSmsUtils.sendSms(phone);
        if (StringUtils.isNotEmpty(code)) {
            try {
                // 将手机号与验证码存入到redis中
                stringRedisTemplate.opsForValue().set(phone,code,100, TimeUnit.HOURS);
            } catch (Exception e) {
                e.printStackTrace();
                return R.error().putObject("系统错误，请稍后重新发送");
            }
            return R.success().putObject("发送成功");
        }

        return R.error().putObject("发送失败");
    }


    /**
     * 用户租车上传证件资料
     * @param uploadDataVo 用户证件资料
     * @return 上传结果
     */
    @Override
    @Transactional
    public R uploadData(UploadDataVo uploadDataVo) {
        // 判断身份证号是否重复
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("card_number",uploadDataVo.getCardNumber());
        ClientUser one = this.getOne(queryWrapper);
        if (one == null) {
            // 根据客户的身份证号，判断性别
            Integer sex = this.getClientSexByCardNumber(uploadDataVo.getCardNumber());
            uploadDataVo.setSex(sex);
            // 根据客户的身份证号，判断地址
            String nativePlace = NativePlace.getNativePlace(Integer.parseInt(uploadDataVo.getCardNumber().substring(0, 6)));
            uploadDataVo.setAddress(nativePlace);
            // 根据用户id，修改用户信息上传用户的证件资料
            boolean upload = clientUserDao.uploadDataById(uploadDataVo);
            if (upload ==  true) {
                // 修改客户的上传证件状态
                ClientUser clientUser = new ClientUser();
                clientUser.setId(uploadDataVo.getId());
                clientUser.setUploadStatus(1);
                boolean b = this.updateById(clientUser);
                if (b == true) {
                    return  R.success().putObject("上传成功");
                }
            }
            return R.error().putObject("上传失败");
        }
        return  R.error().putObject("身份证信息已被认证");
    }

    /**
     * 客户手机号登录
     * @param registerClientVo 工具类
     * @return 登录结果
     */
    @Override
    public R signIn(RegisterClientVo registerClientVo) {
        // 判断手机号是否存在
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone",registerClientVo.getPhone());
        ClientUser one = this.getOne(queryWrapper);
        if (one != null) {
            // 判断验证码是否正确
            if (!StringUtils.equals(registerClientVo.getCode(),
                    stringRedisTemplate.opsForValue().get(
                            registerClientVo.getPhone()))) {
                return R.error().putObject("验证码错误");
            }else {
                return R.success().putObject("登录成功");
            }
        }
        return R.error().putObject("手机号输入有误");
    }

    /**
     * 客户账号密码登录
     * @param registerClientVo 工具类
     * @return 登录结果
     */
    @Override
    public R signInByup(RegisterClientVo registerClientVo) {
        // 判断账号密码是否正确是否存在
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",registerClientVo.getUsername());
        Md5Hash md5Hash = new Md5Hash(registerClientVo.getPassword(), registerClientVo.getUsername(), 2);
        queryWrapper.eq("password",md5Hash.toString());
        ClientUser one = this.getOne(queryWrapper);
        if (one == null) {
            return R.error().putObject("账号或密码输入错误");
        }else {
            // 根据私钥和用户信息，设置当前用户的 token
            String userToken = JwtUtil.generateTokenExpireInMinutes(one, rsaKeyProperties.getPrivatekey(), 24 * 60);
            R r = R.success();
            r.setToken(userToken);
            return r;
        }
    }

    /**
     * 添加/修改用户信息
     * @param clientUser 用户信息实体类
     * @return 添加结果
     */
    @Override
    @Transactional
    public R updateClient(ClientUser clientUser) {
        if (clientUser.getId() == null) {
            // 判断用户账号与手机号与身份账号是否重复
            int one = clientUserDao.getOneByUsernameOrPhone(clientUser.getUsername(),
                    clientUser.getPhone(),clientUser.getCardNumber());
            if (one > 0) {
                return R.error().putObject("账号或手机号或身份证号重复");
            }else {
                // 根据客户的身份证号，获取判断性别
                Integer sex = this.getClientSexByCardNumber(clientUser.getCardNumber());
                clientUser.setSex(sex);
                // 密码加密
                Md5Hash md5Hash = new Md5Hash(clientUser.getPassword(), clientUser.getUsername(), 2);
                clientUser.setPassword(md5Hash.toString());
                // 添加用户信息
                boolean save = this.save(clientUser);
                if (save == true) {
                    return R.success().putObject("添加成功");
                }
            }

        }else {
            // 修改身份证照片，驾照
            boolean b = clientUserDao.updatePhotoById(clientUser);
            if (b == true) {
                // 改变客户上传身份证件状态
                ClientUser user = new ClientUser();
                user.setId(clientUser.getId());
                user.setUploadStatus(1);
                boolean update = this.updateById(user);
                if (update == true) {
                    return R.success().putObject("修改成功");
                }
            }
        }
        return R.error().putObject("失败");
    }

    /**
     * 上传用户其他证件信息
     * @param clientUser 用户信息实体类
     * @return 上传结果
     */
    @Override
    public R updateOtherCard(ClientUser clientUser) {
        ClientUser user = new ClientUser();
        user.setId(clientUser.getId());
        user.setIdType(clientUser.getIdType());
        user.setOtherFrontPhoto(clientUser.getOtherFrontPhoto());
        user.setOtherReversePhoto(clientUser.getOtherReversePhoto());
        int i = clientUserDao.updateById(user);
        if (i >0) {
            return R.success().putObject("上传成功");
        }
        return R.error().putObject("上传失败");
    }

    /**
     * 忘记密码？修改密码
     * @param registerClientVo 工具类
     * @return 修改结果
     */
    @Override
    public R updatePassword(RegisterClientVo registerClientVo) {
        // 根据手机号查询出用户名
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone",registerClientVo.getPhone());
        ClientUser one = this.getOne(queryWrapper);
        if (one == null ){
            return R.error().putObject("账号不存在");
        }else {
            registerClientVo.setUsername(one.getUsername());
            // 判断验证码是否正确
            if (!StringUtils.equals(registerClientVo.getCode(),
                    stringRedisTemplate.opsForValue().get(
                            registerClientVo.getPhone()))) {
                return R.error().putObject("验证码错误");
            }else {
                // 密码加密
                Md5Hash md5Hash = new Md5Hash(registerClientVo.getPassword(), registerClientVo.getUsername(), 2);
                registerClientVo.setPassword(md5Hash.toString());
                // 根据用户手机号修改用户的密码
                boolean b = clientUserDao.updatePassword(registerClientVo);
                if (b == true) {
                    return R.success();
                }else {
                    return R.error();
                }
            }
        }

    }

    /**
     * 解析token
     * @return 用户信息
     */
    @Override
    public R getUserInfo() {
        // 获取 token
        String token = WebUtil.getRequest().getHeader("Token");
        // 解析token
        Payload<ClientUser> infoFromToken = JwtUtil.getInfoFromToken(token,
                rsaKeyProperties.getPublicKey(), ClientUser.class);
        // 获取用户信息
        ClientUser userInfo = infoFromToken.getUserInfo();
        ClientUser clientUser = this.getById(userInfo.getId());
        return R.success().putObject(clientUser);
    }

    /**
     * 用户修改头像
     * @param id 用户id
     * @param headPic 用户头像url
     * @return 修改结果
     */
    @Override
    public R updateHeadPic(Integer id, String headPic) {
        ClientUser clientUser = new ClientUser();
        clientUser.setId(id);
        clientUser.setHeadPic(headPic);
        boolean b = this.updateById(clientUser);
        if (b == true) {
            return R.success();
        }
        return R.error();
    }

    /**
     * 用户修改新密码
     * @param id 用户id
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     * @return 修改结果
     */
    @Override
    public R updateNewPassword(Integer id, String oldPassword, String newPassword) {
        Md5Hash md5Hash = new Md5Hash(oldPassword, this.getById(id).getUsername(), 2);
        // 判断旧密码是否正确
        QueryWrapper<ClientUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        queryWrapper.eq("password",md5Hash.toString());
        ClientUser one = this.getOne(queryWrapper);
        if (one == null) {
            R error = R.error();
            error.setMessage("旧密码错误");
            return error;
        }else {
            // 修改密码
            Md5Hash newMd5 = new Md5Hash(newPassword, this.getById(id).getUsername(), 2);
            ClientUser clientUser = new ClientUser();
            clientUser.setId(id);
            clientUser.setPassword(newMd5.toString());
            boolean b = this.updateById(clientUser);
            if (b == true) {
                return R.success();
            } else {
                return R.error();
            }

        }

    }

    /**
     * 修改客户的账户余额
     * @param updateIntegrationVo 修改客户、余额的工具类
     * @return 上传结果
     */
    @Override
    @Transactional
    public R updateAccountBalance(UpdateIntegrationVo updateIntegrationVo) {
        boolean b = false;
        // 判断状态，是增加余额、减少余额
        if (updateIntegrationVo.getStatus() == 1) {
            // 增加客户的账户余额
            b = clientUserDao.addAccountBalance(updateIntegrationVo);
            updateIntegrationVo.setIntegrationType("账户充值");
            // 增加客户的积分
            this.addIntegration(updateIntegrationVo);
        }else if(updateIntegrationVo.getStatus() == 0) {
            // 减少客户余额
            b = clientUserDao.reduceAccountBalance(updateIntegrationVo);
        }
        if (b == true ) {
            return R.success();
        }
        return R.error();
    }

    /**
     * 获取会员的账户余额和积分数量
     * @param id 客户id
     * @return 数据结果
     */
    @Override
    public R getBag(Integer id) {
        ClientUser clientUser = this.getById(id);
        if (clientUser != null) {
            return R.success().putObject(clientUser);
        }
        return R.error();
    }

    /**
     * 客户使用余额下单，根据客户id，减去客户的账户余额
     * @param clientId 客户id
     * @param paidAmount 实付金额
     * @return 修改结果
     */
    @Override
    public boolean deductAccountBalance(Integer clientId, Double paidAmount) {
        return clientUserDao.deductAccountBalance(clientId,paidAmount);
    }

    /**
     * 增加积分记录
     * @param clientIntegration 修改客户积分工具类
     * @return 增加结果
     */
    private R insertClientIntegration (ClientIntegration clientIntegration) {
        // 添加积分记录
        boolean b = clientIntegrationService.addIntegrationRecord(clientIntegration);
        if (update == true && b == true ) {
            return R.success();
        }
        return R.error();
    }

    /**
     * 获取客户积分
     * @param updateIntegrationVo 修改客户积分工具类
     * @return 客户积分数
     */
    private Integer getIntegration(UpdateIntegrationVo updateIntegrationVo) {
        return this.getById(updateIntegrationVo.getId()).getIntegration();
    }

    /**
     * 获取积分记录实例对象
     * @param updateIntegrationVo 修改客户积分工具类
     * @return 示例对象
     */
    private ClientIntegration  getClientIntegration(UpdateIntegrationVo updateIntegrationVo,
                                                    Integer changeType,Integer history,
                                                    Integer existing) {
        ClientIntegration clientIntegration = new ClientIntegration();
        clientIntegration.setClientId(updateIntegrationVo.getId());
        clientIntegration.setChangeCount(updateIntegrationVo.getIntegration());
        clientIntegration.setIntegrationType(updateIntegrationVo.getIntegrationType());
        clientIntegration.setChangeType(changeType);
        clientIntegration.setHistoryIntegration(history);
        clientIntegration.setExistingIntegration(existing);
        return clientIntegration;

    }

    /**
     * 根据客户的身份证号，判断性别
     * @param cardNumber
     * @return
     */
    private Integer getClientSexByCardNumber(String cardNumber) {
        // 截取代表性别的数字，存入sex
        int sex = Integer.parseInt(String.valueOf(cardNumber.charAt(16)));
        // 判断男女
        if (sex % 2 == 0 ) {
            return 0;
        }else {
            return 1;
        }
    }

}
